package avicit.bdp.dcs.datasource.service;

import avicit.bdp.common.database.ColumnMeta;
import avicit.bdp.common.service.dto.DataSourceDTO;
import avicit.bdp.common.service.service.DataSourceService;
import avicit.bdp.common.service.service.FileOperateCommonService;
import avicit.bdp.common.utils.database.ColumnInfo;
import avicit.bdp.common.utils.database.DBUtils;
import avicit.bdp.common.utils.enums.FileType;
import avicit.bdp.common.utils.enums.JobType;
import avicit.bdp.core.constant.Constants;
import avicit.bdp.dcs.job.dto.JobDTO;
import avicit.bdp.dcs.semistructure.dto.HiveJsonDto;
import avicit.bdp.dcs.semistructure.service.TemplateFieldService;
import avicit.bdp.dcs.task.dto.TaskDTO;
import avicit.bdp.dcs.utils.JdbcConstants;
import com.alibaba.fastjson2.JSON;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author liyb 数据查询服务类
 */
@Service
public class BaseDbService {

    private static final Logger logger = LoggerFactory.getLogger(BaseDbService.class);

    @Autowired
    private DataSourceService dataSourceService;
    @Autowired
    private DatasourceQueryService datasourceQueryService;
    @Autowired
    private DataSourceUtils dataSourceUtils;
    @Autowired
    private TemplateFieldService templateFieldService;

    /**
     * 判断表是否存在
     *
     * @param datasourceId 数据源ID
     * @return
     * @throws Exception 数据源异常
     */
    public boolean isExistTable(String datasourceId, String tableName) throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return DBUtils.isExistTable(dataSourceDTO, tableName);
    }

    /**
     * 删除表
     *
     * @param datasourceId 数据源ID
     * @throws Exception 数据源异常
     */
    public boolean dropTable(String datasourceId, String tableName) throws Exception {
        try {
            DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);

            // 创建的hive表是内部表，所以不必删除对应的hdfs文件

            //            if(Constants.DB_TYPE_HIVE.equals(dataSourceDTO.getDatasourceType())){
            //                // 删除hive表关联的hdfs文件目录
            //                String hiveTableLocation;
            //                if (isExistTable(datasourceId, tableName)) {
            //                    hiveTableLocation =
            // HiveUtils.getHiveTableLocation(DBUtils.buildConnection(dataSourceDTO), tableName);
            //                } else {
            //                    hiveTableLocation = DcsConstants.HIVE_TABLE_LOCATION +
            // tableName.toLowerCase();
            //                }
            //
            //                // 删除hive表关联的hdfs文件目录
            //                boolean isDel = fileOperateCommonService.exists(hiveTableLocation);
            //                logger.info("hive表关联的hdfs文件目录:{},在HDFS中是否存在:{}", hiveTableLocation, isDel);
            //                if (isDel) {
            //                    if (!fileOperateCommonService.delete(hiveTableLocation, true)) {
            //                        logger.error("递归方式删除文件:{}失败", hiveTableLocation.toString());
            //                    }else{
            //                        logger.info("删除hive表关联的hdfs文件目录:{}成功", hiveTableLocation);
            //                    }
            //                }
            //            }

            DBUtils.dropDataTable(dataSourceDTO, tableName);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("删除表:{}失败,原因是:{}", tableName, e.getMessage());
            return false;
        }
    }

    /**
     * 根据数据源ID获取所有表名
     *
     * @param datasourceId 数据源ID
     * @return 所有表名
     * @throws Exception 数据源异常
     */
    public List<String> getTableList(String datasourceId) throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return DBUtils.getTableList(dataSourceDTO);
    }

    /**
     * 获取当前数据库名
     *
     * @param datasourceId 数据源ID
     * @return 当前数据库名
     */
    public String getCurrentSchema(String datasourceId) throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return DBUtils.getCurrentSchema(dataSourceDTO);
    }

    /**
     * 获取表名规则
     *
     * @return
     */
    public String getTableNamePattern(String datasourceId) throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return DBUtils.getTableNamePattern(dataSourceDTO);
    }

    /**
     * 获取表名规则提示
     *
     * @return
     */
    public String getTableNamePatternTips(String datasourceId) throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return DBUtils.getTableNamePatternTips(dataSourceDTO);
    }

    /**
     * sql 语法检查
     *
     * @param datasourceId    数据源ID
     * @param tableName       表名
     * @param filterCondition 过滤条件
     * @return 检查结果
     */
    public Boolean checkGrammar(String datasourceId, String tableName, String filterCondition)
            throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return DBUtils.checkGrammar(dataSourceDTO, tableName, filterCondition);
    }

    /**
     * 根据数据源ID和表名获取所有列
     *
     * @param datasourceId 数据源ID
     * @param tableName    表名
     * @return 所有列
     */
    public List<ColumnInfo> getColumnList(String datasourceId, String tableName) throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return convertColumnInfo(DBUtils.getColumnList(dataSourceDTO, tableName));
    }

    public List<ColumnMeta> getColumnMetaList(String datasourceId, String tableName)
            throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return DBUtils.getColumnList(dataSourceDTO, tableName);
    }

    /**
     * 获取主键列
     *
     * @param datasourceId 数据源ID
     * @param tableName    表名
     * @return 主键列
     */
    public List<ColumnInfo> getPrimaryKeyList(String datasourceId, String tableName)
            throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return convertColumnInfo(DBUtils.getPKColumnList(dataSourceDTO, tableName));
    }

    private List<ColumnInfo> convertColumnInfo(List<ColumnMeta> columnMetaList) {
        List<ColumnInfo> columnInfoList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(columnMetaList)) {
            for (ColumnMeta columnMeta : columnMetaList) {
                ColumnInfo columnInfo = new ColumnInfo();
                columnInfo.setName(columnMeta.getName());
                columnInfo.setType(columnMeta.getColumnType());
                columnInfo.setLength(columnMeta.getColumnLength());
                columnInfo.setPrecision(columnMeta.getColumnPrecision());
                columnInfo.setScale(columnMeta.getScale());
                columnInfo.setComment(columnMeta.getRemark());

                if (columnMeta.getPrimaryKey() != null && columnMeta.getPrimaryKey() == 1) {
                    columnInfo.setIfPrimaryKey(true);
                } else {
                    columnInfo.setIfPrimaryKey(false);
                }

                if (columnMeta.getNullable() != null && columnMeta.getNullable() == 1) {
                    columnInfo.setNullable(true);
                } else {
                    columnInfo.setNullable(false);
                }
                columnInfoList.add(columnInfo);
            }
        }
        return columnInfoList;
    }

    private List<ColumnMeta> convertColumnMeta(List<ColumnInfo> columnInfoList) {
        List<ColumnMeta> columnMetaList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(columnInfoList)) {
            for (ColumnInfo columnInfo : columnInfoList) {
                ColumnMeta columnMeta = new ColumnMeta();
                columnMeta.setName(columnInfo.getName());
                columnMeta.setColumnType(columnInfo.getType());
                columnMetaList.add(columnMeta);
            }
        }
        return columnMetaList;
    }

    /**
     * 获取时间类型的列
     *
     * @param datasourceId 数据源ID
     * @param tableName    表名
     * @return 时间类型的列
     */
    public List<ColumnInfo> getDateTimeColumnList(String datasourceId, String tableName)
            throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return convertColumnInfo(DBUtils.getDateTimeColumnList(dataSourceDTO, tableName));
    }

    /**
     * 统计表数据条数
     *
     * @param datasourceId 数据源ID
     * @param tableName    表名
     * @return 数据条数
     */
    public Long countTableSize(String datasourceId, String tableName, String whereCondition)
            throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return DBUtils.countTableSize(dataSourceDTO, tableName, whereCondition);
    }

    /**
     * 清空表数据
     *
     * @param datasourceId 数据源ID
     * @param tableName    表名
     */
    public boolean clearTable(String datasourceId, String tableName) throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return DBUtils.clearTable(dataSourceDTO, tableName);
    }

    /**
     * 数据源连接测试
     *
     * @param datasourceId 数据源ID
     */
    public boolean dataSourceConnTest(String datasourceId) throws Exception {
        DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(datasourceId);
        return DBUtils.dataSourceConnTest(dataSourceDTO);
    }

    /**
     * 创建表
     */
    public Map<String, String> createTable(
            JobDTO jobDTO, String srcTable, String dstTable, Map<String, String> columnMap)
            throws Exception {
        DataSourceDTO srcDataSource = dataSourceUtils.queryDataSourceById(jobDTO);
        DataSourceDTO dstDataSource =
                dataSourceService.queryDataSourceById(jobDTO.getDstDatasourceId());

        // 1、获取源表字段及类型
        List<ColumnMeta> columnList;
        switch (srcDataSource.getDatasourceType()) {
            case JdbcConstants.HDFS:
            case JdbcConstants.OSS:
                columnList = convertColumnMeta(templateFieldService.getTemplateColumnListById(srcTable));
                break;
            case JdbcConstants.KAFKA:
                columnList = convertColumnMeta(datasourceQueryService.getKafkaTemplateColumnList(srcTable));
                break;
            default:
                columnList = this.getColumnMetaList(jobDTO.getSrcDatasourceId(), srcTable);
                break;
        }
        logger.info("jobId={},源数据表:{},字段为:{}", jobDTO.getId(), srcTable, columnList);

        // 2、 按照列映射配置关系对列名进行转换
        List<ColumnMeta> srcColumnList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(columnList)) {
            for (ColumnMeta columnMeta : columnList) {
                String columnName = columnMap.get(columnMeta.getName());
                if (columnName != null) {
                    columnMeta.setName(columnName);
                    srcColumnList.add(columnMeta);
                }
            }
        }

        // 3、获取主键列
        List<ColumnMeta> keyColumnList = DBUtils.getPKColumnList(srcDataSource, srcTable);
        logger.info("数据表:{},主键为:{}", srcTable, keyColumnList);
        List<ColumnMeta> srcKeyColumnList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(keyColumnList)) {
            for (ColumnMeta columnMeta : keyColumnList) {
                String columnName = columnMap.get(columnMeta.getName());
                if (columnName != null) {
                    columnMeta.setName(columnName);
                    srcKeyColumnList.add(columnMeta);
                }
            }
        }

        Map<String, Object> paramMap = handleParamMap(jobDTO);
        return DBUtils.createTable(
                srcDataSource, dstDataSource, srcColumnList, srcKeyColumnList, dstTable, paramMap);
    }

    /**
     * 解析扩展参数
     *
     * @param jobDTO
     * @return
     */
    private Map<String, Object> handleParamMap(JobDTO jobDTO) {
        Map<String, Object> paramMap = new HashMap<>();

        if (jobDTO.getJobType() == JobType.TRANSFER.getCode()
                || jobDTO.getJobType() == JobType.SYNC.getCode()) {
            if (StringUtils.isNotEmpty(jobDTO.getExtraParams())) {
                HiveJsonDto hiveJsonDto = JSON.parseObject(jobDTO.getExtraParams(), HiveJsonDto.class);
                if (hiveJsonDto.getFileSeparator() != null) {
                    paramMap.put(Constants.FILE_SEPARATOR, hiveJsonDto.getFileSeparator());
                }
                if (hiveJsonDto.getFileType() != null) {
                    paramMap.put(Constants.FILE_TYPE, hiveJsonDto.getFileType());
                }
            }
        } else if (jobDTO.getJobType() == JobType.SEMISTRUCTURE.getCode()) {
            String fileSeparator = Constants.COMMA;
            int fileType = FileType.TEXT.getCode();

            TaskDTO taskDTO = jobDTO.getTaskDTO();
            if (StringUtils.isNotEmpty(taskDTO.getHiveJson())) {
                HiveJsonDto hiveJsonDto = JSON.parseObject(taskDTO.getHiveJson(), HiveJsonDto.class);
                if (hiveJsonDto.getFileSeparator() != null) {
                    fileSeparator = hiveJsonDto.getFileSeparator();
                }

                if (hiveJsonDto.getFileType() != null) {
                    fileType = hiveJsonDto.getFileType();
                }
            }

            paramMap.put(Constants.FILE_SEPARATOR, fileSeparator);
            paramMap.put(Constants.FILE_TYPE, fileType);
        } else if (jobDTO.getJobType() == JobType.STREAM.getCode()) {
            paramMap.put(Constants.FILE_TYPE, FileType.ORC.getCode());
        }

        return paramMap;
    }

}
